package java8.collector.demo02;

import java8.stream.demo01.AbstractDishBeforeTest;
import java8.stream.demo01.Dish;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.stream.Collectors;

/**
 * @author ChangLiang
 * @date 2020/4/23
 */
public class AppTest extends AbstractDishBeforeTest {

    @Test
    @DisplayName("test averaging double")
    public void test() {
        // 这个Optional根本没啥用 不能防止menu=null
        Optional.ofNullable(menu.stream().collect(Collectors.averagingDouble(Dish::getCalories))).ifPresent(System.out::println);
    }

    @Test
    @DisplayName("test averaging double")
    public void test2() {
        // NPE
        menu = null;
        Optional.ofNullable(menu.stream().collect(Collectors.averagingDouble(Dish::getCalories))).ifPresent(System.out::println);
    }

    @Test
    @DisplayName("test")
    public void test40(){

        List<String> tags = new ArrayList<>();
        String tag = null;
        Optional.ofNullable(tag).ifPresent((v) -> tags.addAll(Arrays.asList(v)));
        System.out.println(tags);
    }

    @Test
    @DisplayName("test")
    public void test41(){
        String tag = null;
        System.out.println(tag.toString() + "aaa");

//        Optional.ofNullable(tag).ifPresent(v-> System.out.println(v.toString() + "aaa"));
    }

    @Test
    @DisplayName("test averaging double")
    public void test3() {
        // 0.0
        menu = new ArrayList<>();
        Optional.ofNullable(menu.stream().collect(Collectors.averagingDouble(Dish::getCalories))).ifPresent(System.out::println);
    }

    @Test
    @DisplayName("test averaging double")
    public void test4() {
        // 0.0
        menu = new ArrayList<>();
        System.out.println(menu.stream().collect(Collectors.averagingDouble(Dish::getCalories)));
    }

    @Test
    @DisplayName("test averaging double")
    public void test5() {
        // 0.0
        menu = null;
        System.out.println(Optional.ofNullable(menu).orElse(new ArrayList<>()).stream().collect(Collectors.averagingDouble(Dish::getCalories)));
    }

    @Test
    @DisplayName("test average int")
    public void test6() {
        System.out.println(menu.stream().collect(Collectors.averagingInt(Dish::getCalories)));
    }

    @Test
    @DisplayName("test average long")
    public void test7() {
        System.out.println(menu.stream().collect(Collectors.averagingLong(Dish::getCalories)));
    }

    @Test
    @DisplayName("test collect and then")
    public void test8() {
        String collect = menu.stream().collect(
                Collectors.collectingAndThen(Collectors.averagingDouble(Dish::getCalories),
                        a -> "the average calories is " + a));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test collect and then")
    public void test9() {
        // 返回一个只读的list
        List<Dish> collect = menu.stream().filter(d -> d.getType().equals(Dish.Type.MEAT))
                .collect(Collectors.collectingAndThen(
                        Collectors.toList(),
                        Collections::unmodifiableList
                ));
        collect.add(new Dish("", false, 100, Dish.Type.OTHER));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test counting")
    public void test10() {
        Long collect = menu.stream().collect(Collectors.counting());
        System.out.println(collect);
    }

    @Test
    @DisplayName("test group by function")
    public void test11() {
        Map<Dish.Type, List<Dish>> collect = menu.stream().collect(Collectors.groupingBy(Dish::getType));
        // output: HashMap
        System.out.println(collect.getClass());
        System.out.println(collect);
    }

    /**
     * output: {FISH=375.0, MEAT=633.3333333333334, OTHER=387.5}
     */
    @Test
    @DisplayName("test group by function and collect")
    public void test111() {
        Map<Dish.Type, Double> collect = menu.stream().collect(
                Collectors.groupingBy(
                        Dish::getType,
                        Collectors.averagingInt(Dish::getCalories)));
        // HashMap
        System.out.println(collect.getClass());
        System.out.println(collect);
    }

    /**
     * output: {MEAT=633.3333333333334, FISH=375.0, OTHER=387.5}
     */
    @Test
    @DisplayName("test group by function and supplier and collect")
    public void test12() {
        TreeMap<Dish.Type, Double> collect = menu.stream().collect(
                Collectors.groupingBy(
                        Dish::getType,
                        TreeMap::new,
                        Collectors.averagingInt(Dish::getCalories)));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test summarizing int")
    public void test13(){
        IntSummaryStatistics collect = menu.stream().collect(
                Collectors.summarizingInt(Dish::getCalories)
        );
        // output: IntSummaryStatistics{count=9, sum=4200, min=120, average=466.666667, max=800}
        System.out.println(collect);
    }
    
    @Test
    @DisplayName("test summarizing long")
    public void test131(){
        LongSummaryStatistics collect = menu.stream().collect(Collectors.summarizingLong(Dish::getCalories));
        // output: DoubleSummaryStatistics{count=9, sum=4200.000000, min=120.000000, average=466.666667, max=800.000000}
        System.out.println(collect);
    }

    @Test
    @DisplayName("test summarizing double")
    public void test132(){
        DoubleSummaryStatistics collect = menu.stream().collect(Collectors.summarizingDouble(Dish::getCalories));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test grouping by manongfanshen with function")
    public void test14(){
        ConcurrentMap<Dish.Type, List<Dish>> collect = menu.stream().collect(Collectors.groupingByConcurrent(Dish::getType));
        // ConcurrentHashMap
        System.out.println(collect.getClass());
        System.out.println(collect);
    }

    @Test
    @DisplayName("test grouping by manongfanshen with function and collector")
    public void test15(){
        ConcurrentMap<Dish.Type, Double> collect = menu.stream().collect(Collectors.groupingByConcurrent(
                Dish::getType,
                Collectors.averagingInt(Dish::getCalories)
        ));
        // output: {MEAT=633.3333333333334, FISH=375.0, OTHER=387.5}
        System.out.println(collect);
    }

    @Test
    @DisplayName("test grouping by manongfanshen with function and supplier and collector")
    public void test16() {
        ConcurrentSkipListMap<Dish.Type, Double> collect = menu.stream().collect(Collectors.groupingByConcurrent(
                Dish::getType,
                ConcurrentSkipListMap::new,
                Collectors.averagingInt(Dish::getCalories)
        ));
        // output: {MEAT=633.3333333333334, FISH=375.0, OTHER=387.5}
        System.out.println(collect);
    }

    @Test
    @DisplayName("test joining")
    public void test17(){
        String collect = menu.stream().map(Dish::getName).collect(Collectors.joining());
        // output: porkbeefchickenfrench friesriceseason fruitpizzaprawnssalmon
        System.out.println(collect);
    }

    @Test
    @DisplayName("test joining with delimiter")
    public void test18(){
        String collect = menu.stream().map(Dish::getName).collect(Collectors.joining(","));
        // output: pork,beef,chicken,french fries,rice,season fruit,pizza,prawns,salmon
        System.out.println(collect);
    }

    @Test
    @DisplayName("test joining with delimiter and prefix and suffix")
    public void test19(){
        String collect = menu.stream().map(Dish::getName).collect(Collectors.joining(",", "Names[", "]"));
        // output: Names[pork,beef,chicken,french fries,rice,season fruit,pizza,prawns,salmon]
        System.out.println(collect);
    }
    
    @Test
    @DisplayName("test mapping")
    public void test20(){
        String collect = menu.stream().collect(Collectors.mapping(Dish::getName, Collectors.joining(",")));
        // output: pork,beef,chicken,french fries,rice,season fruit,pizza,prawns,salmon
        System.out.println(collect);
    }

    @Test
    @DisplayName("test maxBy")
    public void test21(){
        Optional<Dish> optional = menu.stream().collect(Collectors.maxBy(Comparator.comparingInt(Dish::getCalories)));
        optional.ifPresent(System.out::println);
    }

    @Test
    @DisplayName("test minBy")
    public void test22(){
        Optional<Dish> optional = menu.stream().collect(Collectors.minBy(Comparator.comparingInt(Dish::getCalories)));
        optional.ifPresent(System.out::println);
    }

    @Test
    @DisplayName("test partitioningBy with predicate")
    public void test23(){
        Map<Boolean, List<Dish>> collect = menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test partitioningBy with predicate and collector")
    public void test24(){
        Map<Boolean, Double> collect = menu.stream().collect(Collectors.partitioningBy(Dish::isVegetarian, Collectors.averagingInt(Dish::getCalories)));
        // output: {false=530.0, true=387.5}
        System.out.println(collect);
    }

    @Test
    @DisplayName("test reducing binary operator and identity")
    public void test25(){
        Integer collect = menu.stream().map(Dish::getCalories).collect(Collectors.reducing(0, Integer::sum));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test reducing binary operator and identity and function")
    public void test26(){
        Integer collect = menu.stream().collect(Collectors.reducing(0, Dish::getCalories, Integer::sum));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test summing double")
    public void test27(){
        Double collect = menu.stream().collect(Collectors.summingDouble(Dish::getCalories));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test summing long")
    public void test28() {
        Long collect = menu.stream().collect(Collectors.summingLong(Dish::getCalories));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test summing int")
    public void test29(){
        Integer collect = menu.stream().collect(Collectors.summingInt(Dish::getCalories));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toCollection")
    public void test30(){
        LinkedList<Dish> collect = menu.stream().filter(d -> d.getCalories() > 600).collect(Collectors.toCollection(LinkedList::new));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toConcurrentMap")
    public void test31(){
        ConcurrentMap<String, Integer> collect = menu.stream().collect(Collectors.toConcurrentMap(
                Dish::getName,
                Dish::getCalories
        ));
        // class java.util.manongfanshen.ConcurrentHashMap
        System.out.println(collect.getClass());
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toConcurrentMap with BinaryOperator")
    public void test32(){
        ConcurrentMap<Dish.Type, Long> collect = menu.stream().collect(Collectors.toConcurrentMap(
                Dish::getType,
                v -> 1L,
                Long::sum
        ));
        // output: {OTHER=4, MEAT=3, FISH=2}
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toConcurrentMap with BinaryOperator and Supplier")
    public void test33(){
        ConcurrentSkipListMap<Dish.Type, Long> collect = menu.stream().collect(Collectors.toConcurrentMap(
                Dish::getType,
                v -> 1L,
                Long::sum,
                ConcurrentSkipListMap::new
        ));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toList")
    public void test34(){
        List<Dish> collect = menu.stream().filter(d -> d.isVegetarian()).collect(Collectors.toList());
        // output: ArrayList
        System.out.println(collect.getClass());
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toSet")
    public void test35(){
        Set<Dish> collect = menu.stream().filter(d -> d.isVegetarian()).collect(Collectors.toSet());
        // output: class java.util.HashSet
        System.out.println(collect.getClass());
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toMap")
    public void test36(){
        Map<String, Integer> collect = menu.stream().collect(Collectors.toMap(Dish::getName, Dish::getCalories));
        // HashMap
        System.out.println(collect.getClass());
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toMap with BinaryOperators")
    public void test37(){
        Map<Dish.Type, Integer> collect = menu.stream().collect(Collectors.toMap(Dish::getType, d -> 1, Integer::sum));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toMap with BinaryOperators and Supplier")
    public void test38(){
        TreeMap<Dish.Type, Integer> collect = menu.stream().collect(Collectors.toMap(Dish::getType, d -> 1, Integer::sum, TreeMap::new));
        System.out.println(collect);
    }

    @Test
    @DisplayName("test toMap for thread safe")
    public void test39(){
        Map<String, Integer> collect = menu.stream().collect(Collectors.collectingAndThen(
                Collectors.toMap(Dish::getName, Dish::getCalories),
                Collections::synchronizedMap
        ));
        System.out.println(collect.getClass());
        System.out.println(collect);
    }
}
